#include "udk_os.h"
#include "commapi.h"
#include "crc16.h"
#include "ActiveSocket.h"

#define CMD_TCP	0x82
#define TCP_HEAD_TAG	0x50

#define ERR_CONNECT_TIMEOUT	-307	//ŽŽœšsocket³¬Ê±(Á¬œÓ³¬Ê±)

#pragma pack(push)
#pragma pack(1)
typedef struct _TCPHeader_{
	unsigned char HeaderTag[2];//2
	unsigned char Cmd;//3
	unsigned char CmdCheck;//4
	unsigned int Len;//8
}TTCPHeader, *PTCPHeader;
#pragma pack(pop)

typedef struct _HH
{
	Mutex* mtx;
	UDK_BOOL bTerminalRead;
	CActiveSocket* mysocket;
	UDK_S32 SendBufferLen;
	UDK_U8 *SendBuffer;
	UDK_S32 RecvBufferLen;
	UDK_U8 *RecvBuffer;
	UDK_U16 quirks;
}	HH, *PH;

static inline UDK_BOOL get_terminal_cond(HH* h){
  AutoMutex autolock(h->mtx);
  return h->bTerminalRead;
}
static inline UDK_BOOL set_terminal_cond(HH* h,UDK_BOOL v){
  AutoMutex autolock(h->mtx);
  h->bTerminalRead=v;
  return h->bTerminalRead;
}


int ReadBody(UDK_HANDLE Handle, int nTotalLen, char *Data, int Count, int TimeOutMS)
{
	HH	*h=(HH *)Handle;
	int msCurrentTime = 0;
	int msStartTime = udk_get_ticks();
	int curlen=0;
	int len = 0;
	while(len<nTotalLen)
	{
		msCurrentTime = udk_get_ticks();
		if((msCurrentTime-msStartTime)>TimeOutMS) {
			UDK_LOGD("tcpcomm_READ data timeout\n");
			break;	
		}
		curlen = h->mysocket->Receive(nTotalLen-len);
		if(curlen>0){
			memcpy(Data + len,h->mysocket->GetData(),curlen);
			if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
				UDK_DUMP((const char*)("response packet <----"),(const char*)(Data + len),curlen);
			}
			len += curlen;
		}else{
			if(CSimpleSocket::SocketEwouldblock==h->mysocket->GetSocketError()){
				continue;
			}		
			if(h->mysocket->GetSocketError()){
				len = -1*h->mysocket->GetSocketError();
				break;
			}
					
		}
	}
	return len;
}


#ifdef COMM_MODULE_AUTOINSTALL
UDK_HANDLE tcpcomm_INIT(PST_COMM_PARAMS param,const char *IPAddress, unsigned short PortNum, int speed)
#else
UDK_HANDLE EMBUDP_INIT(PST_COMM_PARAMS param,const char *IPAddress, unsigned short PortNum, int speed)
#endif
{
	HH	*h = (PH)udk_malloc(sizeof(HH));if(!h) return NULL;
	memset(h,0,sizeof(HH));
	h->mysocket = new CActiveSocket();
	h->mysocket->Initialize();
	h->mysocket->SetReceiveWindowSize(256*1024);
	h->mysocket->SetReceiveTimeout(param->TimeOutMS/1000,(param->TimeOutMS%1000)*1000);
        h->mysocket->SetSendTimeout(param->TimeOutMS/1000,(param->TimeOutMS%1000)*1000);
	UDK_LOGD("tcpcomm connect %s@%d\n",IPAddress,PortNum);
	if(false==h->mysocket->Open((const UDK_U8*)IPAddress,PortNum))
	{
		h->mysocket->Close();
		delete h->mysocket;
		udk_free(h);
		h=NULL;
	}
	if(h){
	    h->mtx=new Mutex();
	    h->SendBufferLen = param->PackageSize + 16;
	    h->SendBuffer = (UDK_U8 *)udk_malloc(h->SendBufferLen);
	    h->RecvBufferLen = param->PackageSize + 16;
	    h->RecvBuffer = (UDK_U8 *)udk_malloc(h->RecvBufferLen);
	    h->quirks = param->Quirks;
	}
	return (UDK_HANDLE)h;
}

#ifdef COMM_MODULE_AUTOINSTALL
UDK_BOOL tcpcomm_CLOSE(UDK_HANDLE Handle)
#else
UDK_BOOL EMBUDP_CLOSE(UDK_HANDLE Handle)
#endif
{
	HH	*h=(HH *)Handle;
	if(h){
	    h->mysocket->Close();
	    udk_free(h->SendBuffer);
	    udk_free(h->RecvBuffer);
 	    delete h->mysocket;
	    delete h->mtx;
	    udk_free(h);
	}
	return UDK_TRUE;
}

static int CheckReadable(HH *h, int TimeOutMS)
{
	if(h->mysocket->Select(TimeOutMS/1000,(TimeOutMS%1000)*1000,0x1))
		return 1;
	return 0;
}

#ifdef COMM_MODULE_AUTOINSTALL
int tcpcomm_SEND(UDK_HANDLE Handle,const char *Data, int Count)
#else
int EMBUDP_SEND(UDK_HANDLE Handle,const char *Data, int Count)
#endif
{
	HH	*h=(HH *)Handle;
	int ret;
	int byteSent=0,sentLen=0,len;
	PTCPHeader ch=(PTCPHeader)(h->SendBuffer);
	int TimeOutMS=5,ms=0;
	memset(h->SendBuffer, 0, sizeof(TTCPHeader));
	ch->HeaderTag[0]=TCP_HEAD_TAG; ch->HeaderTag[1]=TCP_HEAD_TAG;
	ch->Cmd=CMD_TCP;
	ch->CmdCheck=0xFF-CMD_TCP;
	ch->Len=Count;
	memcpy(h->SendBuffer+sizeof(TTCPHeader),Data,Count);
	len = Count + sizeof(TTCPHeader);
	ms = udk_get_ticks();
	while(sentLen<len)
	{   	    
            byteSent = h->mysocket->Send((const UDK_U8*)(h->SendBuffer+sentLen), len-sentLen);
            if(byteSent>0){
            	if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
            		UDK_DUMP((const char*)("command packet ---->"),(const char*)(h->SendBuffer+sentLen),byteSent);
            	}
                sentLen += byteSent;
            }else {
            	UDK_LOGE("send failed ret %d error=%d\n",byteSent,h->mysocket->GetSocketError());
                    if(CSimpleSocket::SocketEwouldblock==h->mysocket->GetSocketError()){
                            continue;
                    }else {
                    	sentLen = h->mysocket->GetSocketError();
                    	if(sentLen>0){
                    		sentLen = -1*sentLen;
                    	}
                    	break;
                    }
            }
            if((udk_get_ticks()-ms)>TimeOutMS) break;
	}
	return sentLen;
}

#ifdef COMM_MODULE_AUTOINSTALL
int tcpcomm_READ(UDK_HANDLE Handle,  char *Data, int Count, int TimeOutMS)
#else
int EMBUDP_READ(UDK_HANDLE Handle,  char *Data, int Count, int TimeOutMS)
#endif
{
	HH	*h=(HH *)Handle;
	int curlen=0,len=0;
	char headbuf[128];
	int ret;
	UDK_DWORD msStartTime=0, msCurrentTime=0;
	msStartTime=udk_get_ticks();
	set_terminal_cond(h,UDK_FALSE);
	while(1)
	{
		msCurrentTime = udk_get_ticks();
		if((msCurrentTime-msStartTime)>TimeOutMS) {
			break;	
		}
		
		if(UDK_TRUE==get_terminal_cond(h)){
			break;
		}

		if(CheckReadable(h,100)) 
		{
			int byterecv=h->mysocket->Receive(sizeof(TTCPHeader));
			if(byterecv>0){
			    memcpy(headbuf,h->mysocket->GetData(),(byterecv>128)?128:byterecv);
			    if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
					UDK_DUMP((const char*)("response packet <----"),(const char*)(headbuf),byterecv);
			    }
			    PTCPHeader tcpch = (PTCPHeader)headbuf;
			    if(byterecv==sizeof(TTCPHeader) && 
				tcpch->HeaderTag[0]==TCP_HEAD_TAG && tcpch->HeaderTag[1]==TCP_HEAD_TAG &&  
				tcpch->Cmd == CMD_TCP && tcpch->CmdCheck == (0xff-CMD_TCP)){
					len = ReadBody(h, tcpch->Len, Data, Count, TimeOutMS);
				  //printf("recv len =%d\n",len);
			      
			    }else
			    {
			    	int maxBufferLen = 64*1024+16;
			    	char tempbuf[64*1024+16] = {0x0};
					int timeoutclear = 1000;
					char* p = NULL;
			    	msStartTime = udk_get_ticks();
					while(udk_get_ticks() - msStartTime < timeoutclear)
					{
						if((curlen = h->mysocket->Receive(maxBufferLen)) > 0)
						{
							PTCPHeader tcpch = (PTCPHeader)(h->mysocket->GetData());
							if(curlen > sizeof(TTCPHeader) && tcpch->HeaderTag[0]==TCP_HEAD_TAG && tcpch->HeaderTag[1]==TCP_HEAD_TAG &&  
				tcpch->Cmd == CMD_TCP && tcpch->CmdCheck == (0xff-CMD_TCP))	
							{
								curlen = curlen-sizeof(TTCPHeader);
								if(curlen >= tcpch->Len)
								{
									len = tcpch->Len;
								}
								else if((len = ReadBody(h,tcpch->Len-curlen, Data+curlen,Count-curlen,TimeOutMS))>0)
								{
									len += curlen;
								}
								break;
							}
							else
							{
								msStartTime = udk_get_ticks();
								UDK_DUMP((const char*)("dirty packet  <----"),(const char*)h->mysocket->GetData(),curlen);
							}
						}
					}
			    	len = 0;	
				}
			    break;
			}
			
		}
	}
	return len;
}
#ifdef COMM_MODULE_AUTOINSTALL
int tcpcomm_SENDCMD(UDK_HANDLE Handle,int Command, char *Data, int Count, PSessionHeader pshd)
#else
int EMBUDP_SENDCMD(UDK_HANDLE Handle,int Command, char *Data, int Count, PSessionHeader pshd)
#endif
{
	HH	*h=(HH *)Handle;
	PProtoHeader pchd = (PProtoHeader)(h->SendBuffer);
	UDK_S32 sendLen = sizeof(TProtoHeader) + CRC_AND_ENDTAG_LEN + Count;
	UDK_S32 sentLen = 0;
	UDK_U8 *p;
	if(Count > h->SendBufferLen-PROTOCOL_AND_SESSION_LEN){
		return ERR_SEND_PACKAGE_BUF_INSUFFICIENT;
	}

	p = h->SendBuffer;
	//fill protocol header
	pchd->StartTag = PROTO_START_TAG;
	pchd->DestAddr = 0x1;
	pchd->Command = Command&0xff;
	pchd->DataSize = Count;
	//fill session header
	if(pshd){		
		memcpy(pchd+1,pshd,sizeof(TSessionHeader));		
		sendLen += sizeof(TSessionHeader);
		pchd->DataSize += sizeof(TSessionHeader);
		if(Count) memcpy((char *)(p+sizeof(TProtoHeader)+sizeof(TSessionHeader)), Data, Count);		
	}else{
		if(Count) memcpy((char*)(p+sizeof(TProtoHeader)),Data,Count);
	}

	SETWORD(p+sizeof(TProtoHeader) + pchd->DataSize, crc16(p+1, sendLen -sizeof(TSessionHeader)));
	*(p+sendLen-PROTOCOL_ENDTAG_LEN) = PROTO_END_TAG;

	sentLen = h->mysocket->Send((const UDK_U8*)(h->SendBuffer), sendLen);
	if(sentLen>0&&(h->quirks&COMM_PARAM_QUIRK_DEBUG)){
		UDK_DUMP((const char*)("command packet ---->"),(const char*)(h->SendBuffer),sentLen);
	}
	return sentLen;
}

#ifdef COMM_MODULE_AUTOINSTALL
int tcpcomm_READCMD(UDK_HANDLE Handle,int *Command,  char *Data, int *Count, int TimeOutMS)
#else
int EMBUDP_READCMD(UDK_HANDLE Handle,int *Command,  char *Data, int *Count, int TimeOutMS)
#endif
{
	HH	*h=(HH *)Handle;
	PProtoHeader pchd = (PProtoHeader)h->RecvBuffer;
	int size = 0, c = 0;
	char *p = (char*)h->RecvBuffer;
	int dataBufInLen = *Count;

	int curLen=0,len=0;
	UDK_DWORD msStartTime=0, msCurrentTime=0;
	int lastTempErrorNo = 0; 

	msStartTime=udk_get_ticks();
	while(1)
	{
		msCurrentTime = udk_get_ticks();
		if((msCurrentTime-msStartTime) > TimeOutMS){
			break;
		}
		//for round
		if(msCurrentTime < msStartTime)	{
			break;
		}
		if(UDK_TRUE==get_terminal_cond(h)) 
			break;

		if(CheckReadable(h, 100)) {
			size=h->mysocket->Receive(sizeof(TProtoHeader));				
			if(size>0){
				memcpy(h->RecvBuffer,h->mysocket->GetData(),size);	
				if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
					UDK_DUMP((const char*)("response packet <----"),(const char*)(h->RecvBuffer + curLen),size);
				}
			
				if (pchd->StartTag == PROTO_START_TAG){
					curLen = sizeof(TProtoHeader);
					len = sizeof(TProtoHeader) + pchd->DataSize + 3;
					if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
						UDK_LOGD("read data len=%d,recvBufLen=%d\n", len, h->RecvBufferLen);
					}
					if(len > h->RecvBufferLen) {
						len = ERR_READ_PACKAGE_BUF_INSUFFICIENT;
								break;
					}
					while(curLen < len){
						msCurrentTime = udk_get_ticks();
						if ((msCurrentTime - msStartTime) > TimeOutMS) break;
						if (msCurrentTime < msStartTime) break;
						size=h->mysocket->Receive(len-curLen);
						if(size>0){
							memcpy(h->RecvBuffer+curLen,h->mysocket->GetData(),size);
							if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
								UDK_DUMP((const char*)("response packet <----"),(const char*)(h->RecvBuffer + curLen),size);		
							}
							curLen += size;
							if (curLen >= len) break;
						}else{
							if(CSimpleSocket::SocketEwouldblock==h->mysocket->GetSocketError())                     		
								continue;
							if(size<0){
							      len=-1*h->mysocket->GetSocketError();								
							      break;
							}
						}                	  
					}
					if (curLen == len){
						UDK_WORD crc16r = GETWORD(p+len -3);
						UDK_WORD crc16l = crc16((UDK_U8*)(p+1),len -4);
						if (crc16r == crc16l){
							*Command = pchd->Command;
							if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
								UDK_LOGD("cmd=%d,datasize=%d,Data buffer len=%d\n",pchd->Command,pchd->DataSize,dataBufInLen );
							}
							if(pchd->DataSize <= dataBufInLen){
							   *Count = pchd->DataSize;
							   memcpy(Data,pchd+1,pchd->DataSize);//data stored error code darcy20110315
							}else if(dataBufInLen >0){
								//out of memory
								*Count = ERR_READ_BUF_INSUFFICIENT;
							}		
					   }else
						   len = ERR_CMD_DATA_CRC_ERROR;
					}else{
						UDK_LOGE("recv data len invalid expected[%d],actual[%d]\n",len,curLen);
						if(len >=0)
							len = ERR_CMD_DATA_INVALID;
						break;
					}
				}else{
					//clear dirty data
					UDK_LOGE("recv data head error\n");
					if(len >=0) len = ERR_CMD_DATA_INVALID;
				}
				break;
			}
	    }
	    udk_msleep(10);	
	}

	return len;
}

#ifdef COMM_MODULE_AUTOINSTALL
int tcpcomm_PREREAD(UDK_HANDLE Handle)
#else
int EMBUDP_PREREAD(UDK_HANDLE Handle)
#endif
{	
	#ifdef COMM_MODULE_AUTOINSTALL
	return tcpcomm_READ(Handle, NULL, 0, 0);
	#else
	return EMBUDP_READ(Handle, NULL, 0, 0);
	#endif
}

#ifdef COMM_MODULE_AUTOINSTALL
int tcpcomm_TERMINALREAD(UDK_HANDLE Handle)
#else
int EMBUDP_TERMINALREAD(UDK_HANDLE Handle)
#endif
{
	HH	*h=(HH *)Handle;
	return set_terminal_cond(h,UDK_TRUE);
}

#ifdef COMM_MODULE_AUTOINSTALL
const char * tcpcomm_NAME(UDK_HANDLE Handle)
#else
const char * EMBUDP_NAME(UDK_HANDLE Handle)
#endif 
{
	return (const char*)"TCP";
}

#ifdef COMM_MODULE_AUTOINSTALL
class CCommModuleTcp{
	public:

		CCommModuleTcp(){
			selfInstall();
		}
		virtual ~CCommModuleTcp(){
		  uninstall_comm_module((const char*)"TCP");
		}
		void selfInstall(void){			
			ST_COMM_MODULE my_comm={
					"TCP",//prot
					"tcpcomm",//libraryname
					tcpcomm_INIT,//init
					tcpcomm_CLOSE,//close
					tcpcomm_SEND,//send
					tcpcomm_READ,//read
					tcpcomm_TERMINALREAD,//terminalread
					tcpcomm_NAME,//name
					NULL,//flush
					NULL,//at->modem
					NULL,//at<-modem		
					NULL,//error
					tcpcomm_SENDCMD,//sendcmd
					tcpcomm_READCMD,//readcmd
			};
			install_comm_module(&my_comm);
		}

};
static CCommModuleTcp commInstance;
#endif

